package com.atguigu.userprofile.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.userprofile.bean.TagCondition;
import com.atguigu.userprofile.bean.TagInfo;
import com.atguigu.userprofile.bean.UserGroup;
import com.atguigu.userprofile.constants.ConstCodes;
import com.atguigu.userprofile.mapper.UserGroupMapper;
import com.atguigu.userprofile.service.TagInfoService;
import com.atguigu.userprofile.service.UserGroupService;
import com.atguigu.userprofile.utils.RedisUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;

import org.apache.catalina.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhangchen
 * @since 2021-05-04
 */
@Service
@Slf4j
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {

    @Autowired
    TagInfoService tagInfoService;

    @Override
    public void saveUserGroup(UserGroup userGroup) {
        //1 需要 条件json 2 需要条件的中文字符串 3 需要创建日期 4 分群人数（需要等待ck计算)
        //1
        List<TagCondition> tagConditionList = userGroup.getTagConditions();
        String conditionjsonstr = JSON.toJSONString(tagConditionList);
        userGroup.setConditionJsonStr(conditionjsonstr);
        //2
        String conditionComment = userGroup.conditionJsonToComment();
        userGroup.setConditionComment(conditionComment);
        //3
        userGroup.setCreateTime(new Date());

        super.saveOrUpdate(userGroup);
    }

    @Override
    //insert into xxxx
    //select
    //bitmapAnd(
    // bitmapAnd(
    //  (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='tg_person_base_gender' and  tag_value = '男')
    //  ,
    //  (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='tg_person_base_agegroup' and tag_value in ('90后','80后' ))
    // )
    // ,
    // (select groupBitmapMergeState(us) from user_tag_value_decimal where tag_code='tg_behavior_order_amount_7d' and tag_value >= 1000)
    //));
    public void genUserGroupUs(UserGroup userGroup) {
        //完成一个insert select  的sql 交给clickhouse进行执行
        // 1 建表 手动
        // 2 insert select 语句 手写

        String insertSelectSQL = getInsertSelectSQL(userGroup);
        super.baseMapper.insertUserGroupCK(insertSelectSQL);
    }



//    @Override
//    public void refreshUserGroupToRedis(UserGroup userGroup ){
//        List<String> usList =
//                super.baseMapper.selectUserGroupFromCK(userGroup.getId().toString());
//
//    }

    @Override
    public void saveUserGroupToRedis(UserGroup userGroup, boolean isRefresh) {
        //1  从ck中读取
        //1.1  生成读取的sql 执行
        List<String> usList = null;
        if(isRefresh){
            String bitmapAndSQL = getBitmapAndSQL(userGroup);
            usList= baseMapper.selectBitmapAndFromCK(bitmapAndSQL);
        }else{
           usList=super.baseMapper.selectUserGroupFromCK(userGroup.getId().toString());
        }


        //2  写入redis
        System.out.println(usList);
        Jedis jedis = RedisUtil.getJedis();

        //1 type?     set(判存）       2 key ? user_group_id    3 value? uslist    4 field/score ? 无
        // 5 写入api sadd    6 读取api  整体读  smembers    单值判存 sismember
        //7 过期时间？ 不加  如果加过期时间 ，要做兜底 再查ck

        String userGroupKey="user_group_id:"+userGroup.getId().toString();

        //写入前 先清理
        jedis.del(userGroupKey);

        String[] us = usList.toArray(new String[]{});
        jedis.sadd(userGroupKey,us);


        userGroup.setUserGroupNum( Long.valueOf(us.length)  );

        jedis.close();


    }

    @Override
    public Long evaluateUserGroup(UserGroup userGroup) {

        String bitmapAndSQL = getBitmapAndSQL(userGroup);
        Long userGroupNum = super.baseMapper.selectUserGroupNum(bitmapAndSQL);

        return userGroupNum;
    }


    //1、 从mysql中取出 条件
    //2、 重新组合sql(现成方法)  提交ck进行计算 写入ck
    //3、结果存redis一份
    @Override
    public void refreshUserGroup(String id, String busiDate) {
      //1
        UserGroup userGroup = this.getById(id);
        String conditionJsonStr = userGroup.getConditionJsonStr();
        List<TagCondition> tagConditionList = JSON.parseArray(conditionJsonStr, TagCondition.class);
        userGroup.setTagConditions(tagConditionList); //补充条件清单
        userGroup.setBusiDate(busiDate);//补充当前业务日期

        //2
        //执行重新写入前 ，要删除旧数据
        baseMapper.deleteUserGroup(userGroup.getId().toString());
        String insertSelectSQL = getInsertSelectSQL(userGroup);
        baseMapper.insertUserGroupCK(insertSelectSQL);

        //3
        // ck 中的 删除和修改 是异步的  问题？ 有可能执行redis时，异步的删除没有真正执行 ，查询出了旧数据 破解？
        // 策略 sleep :  1 睡的时间不确定 2 影响用户体验
         // 策略 异步变同步： 1 影响面太大 2  optimize  table xxx  final   性能影响较大  全表
        //  策略 自旋确认   1  确认间隔
        // 策略： 回调函数 1 需要ck来触发回调 目前 ck没有
        //  策略： 能不能 解耦 ，不依赖异步的处理结果  代价 多进行一次计算  把第二次的计算结果直接写入redis

      //如果是更新操作，就重新根据条件生成sql 在进行查询ck ，把结果直接转为list 送给redis保存
        saveUserGroupToRedis(userGroup,true);//内部自带删除


    }

    public String getInsertSelectSQL(UserGroup userGroup){
        String bitmapAndSQL = getBitmapAndSQL(userGroup);
        StringBuilder insertSQL=new StringBuilder("insert into user_group  select  '")
                .append(userGroup.getId()).append("',").append(bitmapAndSQL);
        return  insertSQL.toString();
    }

    public String getBitmapAndSQL(UserGroup userGroup){
        List<TagCondition> tagConditionList = userGroup.getTagConditions();
        String busiDate = userGroup.getBusiDate();
        Map<String, TagInfo> tagInfoMapWithCode = tagInfoService.getTagInfoMapWithCode();

        StringBuilder bitmapAndSQL=new StringBuilder();
        for (int i = 0; i < tagConditionList.size(); i++) {
            TagCondition tagCondition =  tagConditionList.get(i);
            String subquerySQL = getSubquerySQL(tagCondition, busiDate, tagInfoMapWithCode);
            if(i==0){
                bitmapAndSQL.append("(").append(subquerySQL).append(")");
            }else{
                bitmapAndSQL.insert(0,"bitmapAnd( ").append(",(").append(subquerySQL).append("))");
            }
        }
        return  bitmapAndSQL.toString();


    }




    //根据单个标签筛选条件，来组合出一个子查询
    //select groupBitmapMergeState(us) from user_tag_value_decimal
    // where tag_code='tg_behavior_order_amount_7d' and tag_value >= 1000 and dt='2020-06-14')
    //1、表名：  需要知道标签值类型 -> tag_code ->  tag_info 表里 tag_value_type -->决定表名
    //2、tag_code  要从tagCondition取得
    //3 、操作符号 ： 从tagCondition的 operator  取得，再进行转换
    //4、tagValue :从tagCondition 的tagValues里取得，1、区分一个或者多个，多个要加括号  2、 要通过tag_value_type区分字符还是数字 ，字符串要加单引
    //5、 dt :  busi_date
    public String getSubquerySQL(TagCondition tagCondition, String busiDate, Map<String, TagInfo> tagInfoMap) {
        //1 ,2
        String tagCode = tagCondition.getTagCode();
        TagInfo tagInfo = tagInfoMap.get(tagCode);
        String tagValueType = tagInfo.getTagValueType();
        String tableName = null;
        if (tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_STRING)) {
            tableName = "user_tag_value_string";
        } else if (tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)) {
            tableName = "user_tag_value_decimal";
        } else if (tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_LONG)) {
            tableName = "user_tag_value_long";
        } else if (tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DATE)) {
            tableName = "user_tag_value_date";
        }
      //3
        String operatorEng=tagCondition.getOperator();
        String opt=getConditionOperator(  operatorEng);
       //4
        List<String> tagValueList = tagCondition.getTagValues();
        //类似 .mkString 的方式 把集合元素拼接成字符串
        String tagValues=null;

        if(tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)||tagValueType.equals(ConstCodes.TAG_VALUE_TYPE_LONG)) {
              tagValues = StringUtils.join(tagValueList, ",");
        }else{
             tagValues ="'"+ StringUtils.join(tagValueList, "','")+"'";
        }
        if(tagValueList.size()>1){
            tagValues= "("+tagValues+")";
        }

        // 拼sql
        StringBuilder sql =new StringBuilder();
        sql.append(" select groupBitmapMergeState(us) from ").append(tableName)
                .append( " where tag_code='").append(tagCode.toLowerCase()).append("' ")
                .append(" and tag_value ").append(opt).append(" ").append(tagValues)
                .append(" and dt ='").append(busiDate).append("'") ;
       return    sql.toString();




    }



    // 把中文的操作代码转换为 判断符号
    private  String getConditionOperator(String operator){
        switch (operator){
            case "eq":
                return "=";
            case "lte":
                return "<=";
            case "gte":
                return ">=";
            case "lt":
                return "<";
            case "gt":
                return ">";
            case "neq":
                return "<>";
            case "in":
                return "in";
            case "nin":
                return "not in";
        }
        throw  new RuntimeException("操作符不正确");
    }




}
